/**************************************************************************************************
* Title: SumVector4_16f.cpp
* Author: Gael Huber
* Description: Implements 16-byte aligned 4D vectors
**************************************************************************************************/

/**************************************************************************************************
* Default constructor
**************************************************************************************************/
inline Vector4_16f::Vector4_16f(void)
	: v(_mm_setzero_ps())
{ }

/**************************************************************************************************
* Constructor specifying 64 bit value
**************************************************************************************************/
inline Vector4_16f::Vector4_16f(__m128 f) 
	: v(f)
{ }

/**************************************************************************************************
* Constructor from array
**************************************************************************************************/
inline Vector4_16f::Vector4_16f(const float* f)
	: v(_mm_load_ps(f))
{ }

/**************************************************************************************************
* Constructor specifying values
**************************************************************************************************/
inline Vector4_16f::Vector4_16f(float x, float y, float z, float w)
	: v(_mm_set_ps(w, z, y, x))
{ }

/**************************************************************************************************
* Copy constructor
**************************************************************************************************/
inline Vector4_16f::Vector4_16f(const Vector4_16f& rhs)
	: v(rhs.v)
{ }

/**************************************************************************************************
* Destructor
**************************************************************************************************/
inline Vector4_16f::~Vector4_16f(void)
{ }

/**************************************************************************************************
* Returns the data as a float array
**************************************************************************************************/
inline Vector4_16f::operator float*(void) {
	return (float*) this;
}

/**************************************************************************************************
* Returns the data as a constant float array
**************************************************************************************************/
inline Vector4_16f::operator const float*(void) const {
	return (const float*) this;
}

/**************************************************************************************************
* Assignment operator
**************************************************************************************************/
inline Vector4_16f& Vector4_16f::operator=(const Vector4_16f& rhs) {
	if(this == &rhs)
		return *this;
	v = rhs.v;
	return *this;
}

/**************************************************************************************************
* Adds a vector and returns the result
**************************************************************************************************/
inline Vector4_16f& Vector4_16f::operator+=(const Vector4_16f& rhs) {
	v = _mm_add_ps(v, rhs.v);
	return *this;
}

/**************************************************************************************************
* Subtract a vector and return the result
**************************************************************************************************/
inline Vector4_16f& Vector4_16f::operator-=(const Vector4_16f& rhs) {
	v = _mm_sub_ps(v, rhs.v);
	return *this;
}

/**************************************************************************************************
* Multiply by a scalar and return the result
**************************************************************************************************/
inline Vector4_16f& Vector4_16f::operator*=(float rhs) {
	v = _mm_mul_ps(v, _mm_load1_ps(&rhs));
	return *this;
}

/**************************************************************************************************
* Divide by a scalar and return the result
**************************************************************************************************/
inline Vector4_16f& Vector4_16f::operator/=(float rhs) {
	v = _mm_div_ps(v, _mm_load1_ps(&rhs));
	return *this;
}

/**************************************************************************************************
* Returns an unmodified copy of the object
**************************************************************************************************/
inline Vector4_16f Vector4_16f::operator+(void) const {
	return *this;
}

/**************************************************************************************************
* Returns a negated copy of the vector
**************************************************************************************************/
inline Vector4_16f Vector4_16f::operator-(void) const {
	return Vector4_16f(_mm_mul_ps(v, _mm_set1_ps(-1.0f)));
}

/**************************************************************************************************
* Adds two vectors and returns the result
**************************************************************************************************/
inline Vector4_16f Vector4_16f::operator+(const Vector4_16f& rhs) const {
	return Vector4_16f(_mm_add_ps(v, rhs.v));
}

/**************************************************************************************************
* Takes the difference between two vectors and returns the result
**************************************************************************************************/
inline Vector4_16f Vector4_16f::operator-(const Vector4_16f& rhs) const {
	return Vector4_16f(_mm_sub_ps(v, rhs.v));
}

/**************************************************************************************************
* Multiplies a vector by a scalar and returns the result
**************************************************************************************************/
inline Vector4_16f Vector4_16f::operator*(float rhs) const {
	return Vector4_16f(_mm_mul_ps(v, _mm_load1_ps(&rhs)));
}

/**************************************************************************************************
* Divides a vector by a scalar and returns the result
**************************************************************************************************/
inline Vector4_16f Vector4_16f::operator/(float rhs) const {
	return Vector4_16f(_mm_div_ps(v, _mm_load1_ps(&rhs)));
}

/**************************************************************************************************
* Multiplies a vector by a scalar and returns the result
**************************************************************************************************/
inline Vector4_16f operator*(float lhs, const Vector4_16f& rhs) {
	return Vector4_16f(_mm_mul_ps(_mm_load1_ps(&lhs), rhs.v));
}

/**************************************************************************************************
* Equivalence operator
**************************************************************************************************/
inline bool Vector4_16f::operator==(const Vector4_16f& rhs) const {
	return _mm_movemask_ps(_mm_cmpeq_ps(v, rhs.v)) == 0x0F;
}

/**************************************************************************************************
* Non-equivalence operator
**************************************************************************************************/
inline bool Vector4_16f::operator!=(const Vector4_16f& rhs) const {
	return _mm_movemask_ps(_mm_cmpeq_ps(v, rhs.v)) != 0x0F;
}

/**************************************************************************************************
* Length
**************************************************************************************************/
inline float Vec4Length_16f(const Vector4_16f* pV) {
	__m128 l(_mm_mul_ps(pV->v, pV->v));
	return sqrtf(l.m128_f32[0] + l.m128_f32[1] + l.m128_f32[2] + l.m128_f32[3]);
}

/**************************************************************************************************
* Squared length
**************************************************************************************************/
inline float Vec4LengthSqr_16f(const Vector4_16f* pV) {
	__m128 l(_mm_mul_ps(pV->v, pV->v));
	return l.m128_f32[0] + l.m128_f32[1] + l.m128_f32[2] + l.m128_f32[3];
}

/**************************************************************************************************
* Dot product of two vectors
**************************************************************************************************/
inline float Vec4Dot_16f(const Vector4_16f* pV1, const Vector4_16f* pV2) {
	__m128 l(_mm_mul_ps(pV1->v, pV2->v));
	return l.m128_f32[0] + l.m128_f32[1] + l.m128_f32[2] + l.m128_f32[3];
}

/**************************************************************************************************
* Sum of two vectors
**************************************************************************************************/
inline Vector4_16f* Vec4Add_16f(Vector4_16f* pOut, const Vector4_16f* pV1, const Vector4_16f* pV2) {
	pOut->v = _mm_add_ps(pV1->v, pV2->v);
	return pOut;
}

/**************************************************************************************************
* Difference between two vectors
**************************************************************************************************/
inline Vector4_16f* Vec4Sub_16f(Vector4_16f* pOut, const Vector4_16f* pV1, const Vector4_16f* pV2) {
	pOut->v = _mm_sub_ps(pV1->v, pV2->v);
	return pOut;
}

/**************************************************************************************************
* Returns the minimized vector
**************************************************************************************************/
inline Vector4_16f* Vec4Minimize_16f(Vector4_16f* pOut, const Vector4_16f* pV1, const Vector4_16f* pV2) {
	pOut->v = _mm_min_ps(pV1->v, pV2->v);
	return pOut;
}

/**************************************************************************************************
* Returns the maximized vector
**************************************************************************************************/
inline Vector4_16f* Vec4Maximize_16f(Vector4_16f* pOut, const Vector4_16f* pV1, const Vector4_16f* pV2) {
	pOut->v = _mm_max_ps(pV1->v, pV2->v);
	return pOut;
}

/**************************************************************************************************
* Scale a vector by a scalar value
**************************************************************************************************/
inline Vector4_16f* Vec4Scale_16f(Vector4_16f* pOut, const Vector4_16f* pV, float s) {
	pOut->v = _mm_mul_ps(pV->v, _mm_load1_ps(&s));
	return pOut;
}

/**************************************************************************************************
* Scale a vector by a pre-loaded scalar value
**************************************************************************************************/
inline Vector4_16f* Vec4Scale_16f(Vector4_16f* pOut, const Vector4_16f* pV, const __m128& s) {
	pOut->v = _mm_mul_ps(pV->v, s);
	return pOut;
}

/**************************************************************************************************
* Lerp, V1 + s(V2 - V1)
**************************************************************************************************/
inline Vector4_16f* Vec4Lerp_16f(Vector4_16f* pOut, const Vector4_16f* pV1,
	const Vector4_16f* pV2, float s) {
	pOut->v = _mm_add_ps(pV1->v, _mm_mul_ps(_mm_load1_ps(&s), _mm_sub_ps(pV2->v, pV1->v)));
	return pOut;
}

/**************************************************************************************************
* Lerp, V1 + s(V2 - V1)
**************************************************************************************************/
inline Vector4_16f* Vec4Lerp_16f(Vector4_16f* pOut, const Vector4_16f* pV1,
	const Vector4_16f* pV2, const __m128& s) {
	pOut->v = _mm_add_ps(pV1->v, _mm_mul_ps(s, _mm_sub_ps(pV2->v, pV1->v)));
	return pOut;
}